﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Text;

namespace ExcelConfig.StoreAPI._req
{
    using ExcelConfig.StoreAPI.helpers;
    using SanlieHash.GuoMi;
    internal class multi_content
    {
        internal const string PRE_BOUNDARY = "++++configstore++++";
        internal const string PRE_DISPOSITION = "Content-Disposition: form-data;";
        internal const string DEFAULT_CONTENTTYPE = "Content-Type: multipart/form-data";
        internal const string STREAM_TYPE = "Content-Type: application/octet-stream";
        internal const string BIN_TRANSFER = "Content-Transfer-Encoding: binary";
        internal const string TEXT_TYPE = "Content-Type: text/plain;charset=UTF-8";
        internal const string LINE = "\r\n";
        internal static byte[] line2 = Encoding.UTF8.GetBytes(LINE + LINE);
        internal static byte[] line1 = Encoding.UTF8.GetBytes(LINE);
        internal IDictionary<string, text_content> TextContents { get; set; }
        internal IDictionary<string, stream_content> StreamContents { get; set; }
        internal Guid ID { get; set; }
        internal string Tag { get; set; }
        internal string Boundary { get; set; }
        internal byte[] BoundaryEnd { get; set; }
        internal long _length { get; set; }
        public delegate void ProgressHandle(string name, long now_size, long total_size);
        public multi_content(int bufferLength = 1024 * 64)
        {
            this.ID = Guid.NewGuid();
            this.Tag = new SM3().Make(this.ID.ToByteArray());
            this.Boundary = $"--{PRE_BOUNDARY}{Tag}{LINE}";
            this.BoundaryEnd = Encoding.UTF8.GetBytes($"--{PRE_BOUNDARY}{Tag}--{LINE}");

            _length = BoundaryEnd.Length;
            TextContents = new Dictionary<string, text_content>();
            StreamContents = new Dictionary<string, stream_content>();
        }

        public long ContentLength { get { return _length; } }
        public void AddContent(string name, string value)
        {
            var header = new StringBuilder();
            header.Append(Boundary);
            header.Append($"{PRE_DISPOSITION} name=\"{name}\"");
            header.Append(LINE);
            header.Append(TEXT_TYPE);

            TextContents[name] = new text_content
            {
                header = Encoding.UTF8.GetBytes(header.ToString()),
                value = Encoding.UTF8.GetBytes(value)
            };
            _length += TextContents[name].header.Length;
            _length += TextContents[name].value.Length;
            _length += LINE.Length * 3;

        }
        public long AddFileContent(string name, Stream stream)
        {
            var header = new StringBuilder();
            header.Append(Boundary);
            header.Append($"{PRE_DISPOSITION} name=\"File\"; filename=\"{name}\"");
            header.Append(LINE);
            header.Append(STREAM_TYPE);
            header.Append(LINE);
            header.Append(BIN_TRANSFER);

            StreamContents[name] = new stream_content
            {
                header = Encoding.UTF8.GetBytes(header.ToString()),
                stream = stream
            };

            var filesize = stream.Length;
            _length += StreamContents[name].header.Length;
            _length += filesize;
            _length += LINE.Length * 3;

            return filesize;
        }

        internal void OnBuildingHeader(HttpWebRequest header)
        {
            header.ContentLength = ContentLength;
            header.ContentType = $"multipart/form-data; boundary={PRE_BOUNDARY}{Tag}";
        }
        internal void w_stream(Stream s, byte[] data)
        {
            s.Write(data, 0, data.Length);
        }
        internal void WriteContent(Stream s)
        {
            foreach (var ctx in TextContents)
            {
                w_stream(s, ctx.Value.header);
                w_stream(s, line2);
                w_stream(s, ctx.Value.value);
                w_stream(s, line1);
            }
            if (StreamContents.Count > 0)
            {
                foreach (var ctx in StreamContents)
                {
                    w_stream(s, ctx.Value.header);
                    w_stream(s, line2);

                    helper.ReadAllToMemory(ctx.Value.stream, s);
                    w_stream(s, line1);
                }
            }
            w_stream(s, BoundaryEnd);
        }
        internal struct text_content
        {
            internal byte[] header { get; set; }
            internal byte[] value { get; set; }
        }

        internal struct stream_content
        {
            internal byte[] header { get; set; }
            //internal Stream value { get; set; }
            internal Stream stream { get; set; }
        }
    }
}
